#include "bsonexplorermodel.h"
#include <bson/bsontypes.h>
#include <QDebug>
#include <string>
#include <utility>
using namespace std;
//QMap<mongo::BSONType,QString> BsonExplorerModel::allBSONElementsType;

BsonExplorerModel::BsonExplorerModel(QObject *parent) :
    QAbstractTableModel(parent)
{
}

int BsonExplorerModel::rowCount(const QModelIndex &parent) const
{
    return 3;
}

int BsonExplorerModel::columnCount(const QModelIndex &parent) const
{
    return allBSONElementsVector.size();
}

QVariant BsonExplorerModel::data(const QModelIndex &index, int role) const
{
    try{
        if(!index.isValid())
            return QVariant();
        if(role==Qt::TextAlignmentRole){
            return int(Qt::AlignRight|Qt::AlignVCenter);
        }else if(role== Qt::DisplayRole|| role==Qt::EditRole){

            if(index.column()<allBSONElementsVector.size()){
                if(index.row()==0)
                {
                    return allBSONElementsVector[index.column()].fieldName;
                    //return QString::fromStdString(fieldName);
                }
                else if(index.row()==1)
                {
//                    return allBSONElementsValue[index.column()];
//                    string value=allBSONElementsValue[index.column()];
//                    cout<<"value="<<value<<endl;
//                    return QString::fromStdString(value);
                    return allBSONElementsVector[index.column()].fieldValue.toString();
                }
                else if(index.row()==2)
                {
//                    mongo::BSONType type=allBSONElements[index.column()].type();
//                    map<mongo::BSONType,QString>::iterator iter=
//                            allBSONElementsType.find(type);
//                    if(iter==allBSONElementsType.end())
//                        return tr("UnKnown:%1").arg(type);
//                    return iter->second;
                    return allBSONElementsVector[index.column()].fieldType;
                    //return getDataFromBSONElement(allBSONElements[index.column()]);
                }
                else if(index.row()==3)
                {
                    return QString("bson");
                }else{
                    return QString("bson");
                }
            }else{
                return QString("bson");
            }
        }
    }catch(DBException& e)
    {
         cout<<__FUNCTION__<<":"<<e.what()<<"\n";
         return QString("bson");
    }
    return QVariant();
}

QVariant BsonExplorerModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    try{
        if(role!=Qt::DisplayRole)
            return QVariant();
        if(orientation & Qt::Horizontal)
            return tr("key%1").arg(section);
        else if(orientation & Qt::Vertical)
        {
            if(section==0)
                return tr("key");
            else if(section==1)
                return tr("value");
            else if(section==2)
                return tr("type");
            else if(section==3)
                return tr("string");
        }
    }catch(...)
    {
        qDebug()<<"catch an exception..."<<"\n";
    }
    return QVariant();
}

void BsonExplorerModel::setBSONObject(BSONObj& obj)
{
    initBSONObject(obj);
//    bsonObj=obj;
//    allBSONElements.clear();
//    try{
//        obj.elems(allBSONElements);
//        for(int i=0;i<allBSONElements.size();i++)
//        {
//            allBSONElementsFieldName.push_back(allBSONElements[i].fieldName());
//            allBSONElementsValue.push_back(getDataFromBSONElement(allBSONElements[i]));
//            {
//                QString typeName;
//                mongo::BSONType type=allBSONElements[i].type();

//                if(allBSONElementsType.find(type)==allBSONElementsType.end())
//                    allBSONElementsType.insert(make_pair(type,typeName));
//            }

//            cout<<"allBSONElements["<<i<<"]="<<allBSONElements[i].fieldName()<<endl;
//        }
//    }catch(DBException& e)
//    {
//        cout<<__FUNCTION__<<":"<<e.what()<<"\n";
//    }
//    reset();
}

void BsonExplorerModel::initBSONObject(BSONObj &obj)
{
    try{
        vector<BSONElement> allBSONElements1;
        obj.elems(allBSONElements1);
        for(int i=0;i<allBSONElements1.size();i++)
        {
            BsonModelData bmd;
            bmd.field=allBSONElements1[i];
            bmd.fieldName=QString::fromStdString(allBSONElements1[i].fieldName());
            bmd.fieldType=getBSONObjTypeName(allBSONElements1[i].type());
            bmd.fieldValue=getDataFromBSONElement(allBSONElements1[i]);
            allBSONElementsVector.push_back(bmd);
//            allBSONElementsFieldName.push_back(allBSONElements[i].fieldName());
//            allBSONElementsValue.push_back(getDataFromBSONElement(allBSONElements[i]));
//            {
//                QString typeName;
//                mongo::BSONType type=allBSONElements[i].type();

//                if(allBSONElementsType.find(type)==allBSONElementsType.end())
//                    allBSONElementsType.insert(make_pair(type,typeName));
//            }

            cout<<"allBSONElements["<<i<<"]="<<allBSONElements1[i].fieldName()<<endl;
        }
    }catch(DBException& e)
    {
        cout<<__FUNCTION__<<":"<<e.what()<<"\n";
    }
    reset();
}

QString BsonExplorerModel::getBSONObjTypeName(BSONType type)
{
    QString typeName;
    switch(type) {
        /** smaller than all other types */
        case mongo::MinKey:
            typeName=tr("MinKey");
            break;
        /** end of object */
        case mongo::EOO:
        typeName=tr("EOO");
        break;
        /** double precision floating point value */
        case mongo::NumberDouble:
        typeName=tr("NumberDouble");
        break;
        /** character string, stored in utf8 */
        case mongo::String:
        typeName=tr("String");
        break;
        /** an embedded object */
        case mongo::Object:
        typeName=tr("Object");
        break;
        /** an embedded array */
        case mongo::Array:
        typeName=tr("Array");
        break;
        /** binary data */
        case mongo::BinData:
        typeName=tr("BinData");
        break;
        /** Undefined type */
        case mongo::Undefined:
        typeName=tr("Undefined");
        break;
        /** ObjectId */
        case mongo::jstOID:
        typeName=tr("jstOID");
        break;
        /** boolean type */
        case mongo::Bool:
        typeName=tr("Bool");
        break;
        /** date type */
        case mongo::Date:
        typeName=tr("Date");
        break;
        /** null type */
        case mongo::jstNULL:
        typeName=tr("jstNULL");
        break;
        /** regular expression, a pattern with options */
        case mongo::RegEx:
        typeName=tr("RegEx");
        break;
        /** deprecated / will be redesigned */
        case mongo::DBRef:
        typeName=tr("DBRef");
        break;
        /** deprecated / use CodeWScope */
        case mongo::Code:
        typeName=tr("Code");
        break;
        /** a programming language (e.g., Python) symbol */
        case mongo::Symbol:
        typeName=tr("Symbol");
        break;
        /** javascript code that can execute on the database server, with SavedContext */
        case mongo::CodeWScope:
        typeName=tr("CodeWScope");
        break;
        /** 32 bit signed integer */
        case mongo::NumberInt :
        typeName=tr("NumberInt");
        break;
        /** Updated to a Date with value next OpTime on insert */
        case mongo::Timestamp :
        typeName=tr("Timestamp");
        break;
        /** 64 bit integer */
        case mongo::NumberLong://JSTypeMax
        typeName=tr("NumberLong");
        break;
        /** larger than all other types */
        case mongo::MaxKey:
        typeName=tr("MaxKey");
        break;
    default:
        return tr("UnKnown:%1").arg(type);
    };
    return typeName;
}

QVariant BsonExplorerModel::getDataFromBSONElement(BSONElement bsonElement)
{
    try{
    switch(bsonElement.type())
    {
    case mongo::MinKey:
        return tr("MinKey");
    case mongo::EOO:
        return tr("EOO");
    case mongo::NumberDouble:
        return tr("%1").arg(bsonElement.Double());
    case mongo::String:
        return tr(bsonElement.String().c_str());
    case mongo::Object:
        return tr("Object");
    case mongo::Array:
        {
            vector<BSONElement> elems=bsonElement.Array();
            QString arrayStr="[";
            for(int i=0;i<elems.size();i++)
            {
                arrayStr+=tr("%1").arg(getDataFromBSONElement(elems[i]).toString());
                if(i<elems.size()-1)
                    arrayStr+=tr(",");
            }
            arrayStr+=tr("]");
            return arrayStr;
        }
    case mongo::BinData:
        {
            int len=0;
            return tr(bsonElement.binData(len));
        }
    case mongo::Undefined:
        {
            return tr("Undefined");
        }
    case mongo::jstOID:
        {
            return tr(bsonElement.OID().toString().c_str());
        }
    case mongo::Bool:
        return tr("%1").arg(bsonElement.Bool());
    case mongo::Date:
        return tr(bsonElement.Date().toString().c_str());
    case mongo::jstNULL:
        {
            if(bsonElement.isNull())
                return tr("NULL");
        }
    case mongo::RegEx:
        return tr(bsonElement.regex());
    case mongo::DBRef:
        return tr(bsonElement.dbrefNS());
    case mongo::Code:
        return tr(bsonElement.codeWScopeCode());
    case mongo::Symbol:
        return tr("Symbol");
    case mongo::CodeWScope:
        return tr(bsonElement.codeWScopeScopeData());
    case mongo::NumberInt:
        return tr("%1").arg(bsonElement.Int());
    case mongo::Timestamp:
        return tr(bsonElement.timestampTime().toString().c_str());
    case mongo::NumberLong:
        return tr("%1").arg(bsonElement.Long());
    case mongo::MaxKey:
        return tr("Undefined");
    }
    }catch(DBException& e)
    {
        qDebug()<<"convert exeption..."<<"\n";
    }
    return QVariant();
}

